{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 内存机制\n",
    "\n",
    "## `make_object`\n",
    "\n",
    "```c++\n",
    "template <typename T, typename... Args>\n",
    "inline ObjectPtr<T> make_object(Args&&... args);\n",
    "```\n",
    "\n",
    "`make_object`，它的作用是使用默认的分配器来分配一个对象。\n",
    "\n",
    "参数列表中包括了三个参数：\n",
    "- `T`：节点类型，是一个模板参数；\n",
    "- `Args`：构造函数的参数，也是一个模板参数；\n",
    "- `args`：可变参数，表示传递给构造函数的参数。\n",
    "\n",
    "函数返回值类型为`ObjectPtr<T>`，即指向类型为 `T` 的对象的指针。\n",
    "\n",
    "在函数体内部，使用了模板展开的方式传递参数给构造函数，最终调用了 TVM 中的默认分配器来分配新的对象，并返回该对象的指针。\n",
    "\n",
    "```c++\n",
    "template <typename T, typename... Args>\n",
    "inline ObjectPtr<T> make_object(Args&&... args) {\n",
    "  return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`make_object` 函数使用了名为 `SimpleObjAllocator` 的对象来进行对象的分配和释放操作。在函数内部，首先调用了 `SimpleObjAllocator` 的默认构造函数来创建 `SimpleObjAllocator` 对象。然后，通过调用 `make_object<T>(std::forward<Args>(args)...)` 来创建类型为 `T` 的对象，并将传入的参数进行完美转发（`std::forward<Args>(args)...`）传递给对象的构造函数。最后，将创建的对象指针作为函数的返回值返回。\n",
    "\n",
    "这个函数的作用是简化对象的创建过程，通过传入类型和参数，可以快速创建指定类型的对象，并返回其指针。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `make_inplace_array_object`\n",
    "\n",
    "模板函数 `make_inplace_array_object`，它接受可变参数 `size_t num_elems, Args&&... args`，并返回 `ObjectPtr<ArrayType>` 类型的对象指针。\n",
    "\n",
    "该函数使用了名为 `SimpleObjAllocator` 的对象来进行对象的分配和释放操作。在函数内部，首先调用了 `SimpleObjAllocator` 的默认构造函数来创建 `SimpleObjAllocator` 对象。然后，通过调用 `make_inplace_array<ArrayType, ElemType>(num_elems, std::forward<Args>(args)...)` 来创建指定元素个数的数组对象，并将传入的元素个数和参数进行完美转发（`std::forward<Args>(args)...`）传递给数组对象的构造函数。最后，将创建的数组对象指针作为函数的返回值返回。\n",
    "\n",
    "这个函数的作用是简化数组对象的创建过程，通过传入元素个数和参数，可以快速创建一个指定元素个数的数组对象，并返回其指针。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{admonition} 内存分配器的设计说明\n",
    "1. 允许在必要时交换分配器模式，这意味着 TVM 的内存分配器可以根据不同的需求和场景进行灵活的切换。\n",
    "2. 一些可能的未来优化方向：\n",
    "    - 使用 Arena 分配器，将内存所有权交给 Arena（deleter_= nullptr），这种分配器可以更好地管理内存块的大小和生命周期；\n",
    "    - 线程本地对象池：为每个大小和对齐要求创建一个对象池，这种优化可以提高多线程程序的性能；\n",
    "    - 通过对象的类型进行特化，为每个对象提供特定的分配器，这种优化可以更好地满足不同类型对象的内存需求。\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `ObjAllocatorBase`\n",
    "\n",
    "`ObjAllocatorBase` 模板类，它是所有对象分配器的基类。该类使用了 Curiously recurring template pattern，即在派生类中实现与基类相同的模板函数。\n",
    "\n",
    "该类中有两个模板函数：\n",
    "- `make_object(Args&&... args)`：用于创建新的对象，其中 `T` 是要分配的类型，`Args` 是构造函数的参数类型，`args` 是实际传入的参数。该函数首先获取派生类中的 `Handler` 模板类，然后使用 `Handler::New()` 方法创建新的对象，并设置对象的 `type_index_` 和 `deleter_`，最后返回指向该对象的指针。\n",
    "- `make_inplace_array(size_t num_elems, Args&&... args)`：用于创建指定元素个数的数组，其中 `ArrayType` 是数组类型，`ElemType` 是数组元素的类型，`num_elems` 是数组元素的数量，`args` 是实际传入的参数。该函数首先获取派生类中的 `ArrayHandler` 模板类，然后使用 `Handler::New()` 方法创建新的对象数组，并设置数组的 `type_index_` 和 `deleter_`，最后返回指向该对象数组的指针。\n",
    "\n",
    "这两个函数都使用了静态断言（static_assert）来确保创建的对象类型为 `Object` 或其派生类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `SimpleObjAllocator`\n",
    "\n",
    "`SimpleObjAllocator`，它继承自 `ObjAllocatorBase<SimpleObjAllocator>`（这是 C++ 的 CRTP 实现静态多态技巧）。该分配器使用 `new/delete` 来管理对象的内存分配和释放。\n",
    "\n",
    "该分配器中定义了两个嵌套模板类：`Handler` 和 `ArrayHandler`。`Handler` 用于创建单个对象，而 `ArrayHandler` 用于创建对象数组。这两个类都使用了静态断言（static_assert）来确保对象类型符合特定的要求。\n",
    "\n",
    "在 `Handler` 类中，使用 `new` 运算符创建新的对象，并使用 `std::aligned_storage` 来分配具有特定对齐要求的存储空间。然后，使用 `placement new` 将新对象放置在已分配的存储空间中，并调用构造函数初始化对象。最后，返回指向新对象的指针。\n",
    "\n",
    "在 `ArrayHandler` 类中，使用 `new` 运算符创建指定元素个数的新对象数组，并使用 `std::aligned_storage` 来分配具有特定对齐要求的存储空间。然后，使用 `placement new` 将新对象放置在已分配的存储空间中，并调用构造函数初始化数组。最后，返回指向新对象数组的指针。\n",
    "\n",
    "在 `Deleter` 函数中，使用 `delete` 运算符释放对象或对象数组的内存。对于对象数组，需要使用 `delete[]` 运算符释放整个数组的内存。\n",
    "\n",
    "总之，这段代码实现了简单的对象分配器，可以用于在程序中动态分配和释放对象或对象数组的内存。"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
